#include "Engine.h"
#include "UI.h"
#include "InputManager.h"
#include "Game.h"
#include "MainMenuDlg.h"

// CEGUI includes
#include "CEGUISystem.h"
#include "CEGUIInputEvent.h"
#include "CEGUIWindow.h"
#include "CEGUIWindowManager.h"
#include "CEGUISchemeManager.h"
#include "CEGUIFontManager.h"
#include "elements/CEGUIFrameWindow.h"
using namespace Ogre;
template<> CEngine* Ogre::Singleton<CEngine>::ms_Singleton = 0;
//-------------------------------------------------------------------------------------
CEngine::CEngine()
//: mUI(NULL)
{
}

//-------------------------------------------------------------------------------------
CEngine::~CEngine()
{
    //	PlatformManager::getSingleton().destroyInputReader( mInputDevice );
    delete Root::getSingletonPtr();
}

//-------------------------------------------------------------------------------------
bool CEngine::initOgreCore()
{
    // Show the configuration dialog and initialise the system
    // You can skip this and use root.restoreConfig() to load configuration
    // settings if you were sure there are valid ones saved in ogre.cfg
    if(Root::getSingletonPtr()->showConfigDialog())
    {
        // If returned true, user clicked OK so initialise
        // Here we choose to let the system create a default rendering window by passing 'true'
        mWindow = Root::getSingletonPtr()->initialise(true);
        return true;
    }
    else
    {
        return false;
    }
}
//-------------------------------------------------------------------------------------
void CEngine::createSceneManager()
{
    // Create the SceneManager, in this case a generic one
    mSceneMgr = Root::getSingletonPtr()->createSceneManager(ST_GENERIC, "Main");
}
//-------------------------------------------------------------------------------------
void CEngine::createCamera()
{
    // Create the camera
    mCamera = mSceneMgr->createCamera("PlayerCam");

    // Position it at 500 in Z direction
    mCamera->setPosition(Vector3(0,0,80));
    // Look back along -Z
    mCamera->lookAt(Vector3(0,0,-300));
    mCamera->setNearClipDistance(5);

}
//-------------------------------------------------------------------------------------
void CEngine::createFrameListener()
{
    mDebugOverlay = OverlayManager::getSingleton().getByName("Core/DebugOverlay");
    mRotateSpeed = 36;
    mMoveSpeed = 100;

    //mInputDevice = PlatformManager::getSingleton().createInputReader();
    //mInputDevice->initialise(mWindow,true, true);

    mStatsOn = true;
    mNumScreenShots = 0;
    mTimeUntilNextToggle = 0;
    mSceneDetailIndex = 0;
    mMoveScale = 0.00f;
    mRotScale = 0.0f;
    mTranslateVector = Vector3::ZERO;
    mAniso = 1;
    mFiltering = TFO_BILINEAR;

    showDebugOverlay(true);
    Root::getSingletonPtr()->addFrameListener(this);
}
//-------------------------------------------------------------------------------------
void CEngine::destroyScene()
{
}
//-------------------------------------------------------------------------------------
void CEngine::createViewports()
{
    // Create one viewport, entire window
    Viewport* vp = mWindow->addViewport(mCamera);
    vp->setBackgroundColour(ColourValue(0,0,0));

    // Alter the camera aspect ratio to match the viewport
    mCamera->setAspectRatio(
        Real(vp->getActualWidth()) / Real(vp->getActualHeight()));
}
//-------------------------------------------------------------------------------------
void CEngine::addResourceLocations()
{
    // Load resource paths from config file
    ConfigFile cf;
#ifdef _DEBUG
    cf.load("resources_d.cfg");
#else
    cf.load("resources.cfg");
#endif
    // Go through all sections & settings in the file
    ConfigFile::SectionIterator seci = cf.getSectionIterator();

    String secName, typeName, archName;
    while (seci.hasMoreElements())
    {
        secName = seci.peekNextKey();
        ConfigFile::SettingsMultiMap *settings = seci.getNext();
        ConfigFile::SettingsMultiMap::iterator i;
        for (i = settings->begin(); i != settings->end(); ++i)
        {
            typeName = i->first;
            archName = i->second;
            ResourceGroupManager::getSingleton().addResourceLocation(
                archName, typeName, secName);
        }
    }
}
//-------------------------------------------------------------------------------------
void CEngine::createResourceListener()
{

}
//-------------------------------------------------------------------------------------
void CEngine::initResources()
{
    ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
}
//-------------------------------------------------------------------------------------
void CEngine::go()
{
    if (!initialise())
        return;

    // showDebugOverlay(true); // Already called on CEngine::createFrameListener().

    Root::getSingletonPtr()->startRendering();

    // clean up
    destroyScene();
}
//-------------------------------------------------------------------------------------
bool CEngine::initialise()
{
    //------------------------------------------------------------------------------
    // initialise Ogre and Window
    //Ogre::LogManager* logMgr = OGRE_NEW Ogre::LogManager;
    //logMgr->createLog("DefaultLog", true, false, false);
    new Root();
    LogManager::getSingleton().setLogDetail(LL_NORMAL);
    addResourceLocations();

    // if we cannot initialise Ogre, just abandon the whole deal
    if ( !initOgreCore() ) return false;

    createSceneManager();
    createCamera();
    createViewports();

    //------------------------------------------------------------------------------
    // initialise GUI
    //mUI = UI::getSingletonPtr();
    //mUI->init(mWindow);
    //------------------------------------------------------------------------------
    // initialise Input
    mInputMgr = CInputManager::getSingletonPtr();
    mInputMgr->initialise( mWindow );
    //mInputMgr->addMouseListener(mUI, "CEGUI");
    //mInputMgr->addKeyListener(mUI, "CEGUI");
    mInputMgr->getJoystick( 1 );
    //------------------------------------------------------------------------------
    // initialise Game
    //mGame = CGame::getSingletonPtr();
    
    //------------------------------------------------------------------------------
    // Link all parts, create the main menu
    //mMainMenu = std::tr1::shared_ptr<MainMenuDlg>(new MainMenuDlg(UI::getSystem(), 
    //    UI::getWindow(), mGame));
    //------------------------------------------------------------------------------
    // Load game resources

    //------------------------------------------------------------------------------
    // Configure game





    // Set default mipmap level (NB some APIs ignore this)
    TextureManager::getSingleton().setDefaultNumMipmaps(5);

    // Create any resource listeners (for loading screens)
    createResourceListener();
    // Initialise resources
    initResources();

    // Create the scene
    createScene();

    createFrameListener();

    return true;
};
//-------------------------------------------------------------------------------------
void CEngine::updateStats()
{
    static String currFps = "Current FPS: ";
    static String avgFps = "Average FPS: ";
    static String bestFps = "Best FPS: ";
    static String worstFps = "Worst FPS: ";
    static String tris = "Triangle Count: ";

    // update stats when necessary
    try {
        //OverlayElement* guiAvg = OverlayManager::getSingleton().getOverlayElement("Core/AverageFps");
        //OverlayElement* guiCurr = OverlayManager::getSingleton().getOverlayElement("Core/CurrFps");
        //OverlayElement* guiBest = OverlayManager::getSingleton().getOverlayElement("Core/BestFps");
        //OverlayElement* guiWorst = OverlayManager::getSingleton().getOverlayElement("Core/WorstFps");

        //const RenderTarget::FrameStats& stats = mWindow->getStatistics();

        //guiAvg->setCaption(avgFps + StringConverter::toString(stats.avgFPS));
        //guiCurr->setCaption(currFps + StringConverter::toString(stats.lastFPS));
        //guiBest->setCaption(bestFps + StringConverter::toString(stats.bestFPS)
        //    +" "+StringConverter::toString(stats.bestFrameTime)+" ms");
        //guiWorst->setCaption(worstFps + StringConverter::toString(stats.worstFPS)
        //    +" "+StringConverter::toString(stats.worstFrameTime)+" ms");

        //OverlayElement* guiTris = OverlayManager::getSingleton().getOverlayElement("Core/NumTris");
        //guiTris->setCaption(tris + StringConverter::toString(stats.triangleCount));

        ////OverlayElement* guiDbg = OverlayManager::getSingleton().getOverlayElement("Core/DebugText");
        ////guiDbg->setCaption(mWindow->getDebugText());
    }
    catch(...)
    {
        // ignore
    }
}

//-------------------------------------------------------------------------------------
bool CEngine::processUnbufferedKeyInput(const Ogre::FrameEvent& evt)
{
    using namespace Ogre;
    using namespace OIS;
    OIS::Keyboard *mInputDevice = CInputManager::getSingletonPtr()->getKeyboard();
    if (mInputDevice->isKeyDown(KC_A))
    {
        // Move camera left
        mTranslateVector.x = -mMoveScale;
    }

    if (mInputDevice->isKeyDown(KC_D))
    {
        // Move camera RIGHT
        mTranslateVector.x = mMoveScale;
    }

    /* Move camera forward by keypress. */
    if (mInputDevice->isKeyDown(KC_UP) || mInputDevice->isKeyDown(KC_W) )
    {
        mTranslateVector.z = -mMoveScale;
    }

    /* Move camera backward by keypress. */
    if (mInputDevice->isKeyDown(KC_DOWN) || mInputDevice->isKeyDown(KC_S) )
    {
        mTranslateVector.z = mMoveScale;
    }

    if (mInputDevice->isKeyDown(KC_PGUP) || mInputDevice->isKeyDown(KC_E))
    {
        // Move camera up
        mTranslateVector.y = mMoveScale;
    }

    if (mInputDevice->isKeyDown(KC_PGDOWN)|| mInputDevice->isKeyDown(KC_Q))
    {
        // Move camera down
        mTranslateVector.y = -mMoveScale;
    }

    if (mInputDevice->isKeyDown(KC_RIGHT))
    {
        mCamera->yaw(-mRotScale);
    }

    if (mInputDevice->isKeyDown(KC_LEFT))
    {
        mCamera->yaw(mRotScale);
    }

    if( mInputDevice->isKeyDown( KC_ESCAPE) )
    {
        return false;
    }

    if (mInputDevice->isKeyDown(KC_F) && mTimeUntilNextToggle <= 0)
    {
        mStatsOn = !mStatsOn;
        showDebugOverlay(mStatsOn);

        mTimeUntilNextToggle = 1;
    }
    //if (mInputDevice->isKeyDown(KC_T) && mTimeUntilNextToggle <= 0)
    //{
    //    switch(mFiltering)
    //    {
    //    case TFO_BILINEAR:
    //        mFiltering = TFO_TRILINEAR;
    //        mAniso = 1;
    //        break;
    //    case TFO_TRILINEAR:
    //        mFiltering = TFO_ANISOTROPIC;
    //        mAniso = 8;
    //        break;
    //    case TFO_ANISOTROPIC:
    //        mFiltering = TFO_BILINEAR;
    //        mAniso = 1;
    //        break;
    //    default:
    //        break;
    //    }
    //    MaterialManager::getSingleton().setDefaultTextureFiltering(mFiltering);
    //    MaterialManager::getSingleton().setDefaultAnisotropy(mAniso);


    //    showDebugOverlay(mStatsOn);

    //    mTimeUntilNextToggle = 1;
    //}

    //if (mInputDevice->isKeyDown(KC_SYSRQ) && mTimeUntilNextToggle <= 0)
    //{
    //    char tmp[20];
    //    sprintf(tmp, "screenshot_%d.png", ++mNumScreenShots);
    //    mWindow->writeContentsToFile(tmp);
    //    mTimeUntilNextToggle = 0.5;
    //    mWindow->setDebugText(String("Wrote ") + tmp);
    //}

    //if (mInputDevice->isKeyDown(KC_R) && mTimeUntilNextToggle <=0)
    //{
    //    mSceneDetailIndex = (mSceneDetailIndex+1)%3 ;
    //    switch(mSceneDetailIndex) {
    //            case 0 : mCamera->setPolygonMode(PM_SOLID) ; break ;
    //            case 1 : mCamera->setPolygonMode(PM_WIREFRAME) ; break ;
    //            case 2 : mCamera->setPolygonMode(PM_POINTS) ; break ;
    //    }
    //    mTimeUntilNextToggle = 0.5;
    //}

    //static bool displayCameraDetails = false;
    //if (mInputDevice->isKeyDown(KC_P) && mTimeUntilNextToggle <= 0)
    //{
    //    displayCameraDetails = !displayCameraDetails;
    //    mTimeUntilNextToggle = 0.5;
    //    if (!displayCameraDetails)
    //        mWindow->setDebugText("");
    //}
    //if (displayCameraDetails)
    //{
    //    // Print camera details
    //    mWindow->setDebugText("P: " + StringConverter::toString(mCamera->getDerivedPosition()) + " " +
    //        "O: " + StringConverter::toString(mCamera->getDerivedOrientation()));
    //}

    // Return true to continue rendering
    return true;
}
//-------------------------------------------------------------------------------------
bool CEngine::processUnbufferedMouseInput(const Ogre::FrameEvent& evt)
{
    using namespace Ogre;
    using namespace OIS;

    /* Rotation factors, may not be used if the second mouse button is pressed. */

    /* If the second mouse button is pressed, then the mouse movement results in
    sliding the camera, otherwise we rotate. */
    OIS::Mouse *mInputDevice = CInputManager::getSingletonPtr()->getMouse();
    const OIS::MouseState &mouse = mInputDevice->getMouseState();
    if( mouse.buttonDown(MB_Right) )
    {
    	mTranslateVector.x += mouse.X.rel * 0.13f;
    	mTranslateVector.y -= mouse.Y.rel * 0.13f;
    }
    else if (mouse.buttonDown(MB_Left))
    {
    	mRotX = Degree(-mouse.X.rel * 0.13f);
    	mRotY = Degree(-mouse.Y.rel * 0.13f);
    }
    //mTranslateVector.x += 0;
    //mTranslateVector.y -= 0;
    //mRotX = Degree(-mouse.X.rel * 0.13f);
    //mRotY = Degree(-mouse.Y.rel * 0.13f);

    return true;
}
//-------------------------------------------------------------------------------------
void CEngine::moveCamera()
{

    // Make all the changes to the camera
    // Note that YAW direction is around a fixed axis (freelook style) rather than a natural YAW (e.g. airplane)
    mCamera->yaw(mRotX);
    mCamera->pitch(mRotY);
    mCamera->moveRelative(mTranslateVector);


}
//-------------------------------------------------------------------------------------
void CEngine::showDebugOverlay(bool show)
{
    if (mDebugOverlay)
    {
        if (show)
        {
            mDebugOverlay->show();
        }
        else
        {
            mDebugOverlay->hide();
        }
    }
}
//-------------------------------------------------------------------------------------
bool CEngine::frameStarted(const FrameEvent& evt)
{
    if(mWindow->isClosed())
        return false;

    if(mInputMgr) {
        mInputMgr->capture();
    }
    //if (mGame->getCurrentState() == SHUTDOWN) {
    //    return false;
    //}
    //if (mGame->getCurrentState() == STARTUP) {
    //    UI::getSingleton().load("./layout/main_menu.lua");
    //    mGame->requestStateChange(SIMULATION);
    //}

        mTimeUntilNextToggle -= evt.timeSinceLastFrame;

    // If this is the first frame, pick a speed
    if (evt.timeSinceLastFrame == 0)
    {
        mMoveScale = 1;
        mRotScale = 0.1f;
    }
    // Otherwise scale movement units by time passed since last frame
    else
    {
        // Move about 100 units per second,
        mMoveScale = mMoveSpeed * evt.timeSinceLastFrame;
        // Take about 10 seconds for full rotation
        mRotScale = mRotateSpeed * evt.timeSinceLastFrame;
    }
    mRotX = 0;
    mRotY = 0;
    mTranslateVector = Vector3::ZERO;

    //mInputDevice->capture();

    if (processUnbufferedKeyInput(evt) == false)
    {
        return false;
    }
    if (processUnbufferedMouseInput(evt) == false)
    {
        return false;
    }

    moveCamera();

    return true;
}
//-------------------------------------------------------------------------------------
bool CEngine::frameEnded(const FrameEvent& evt)
{
    updateStats();
    return true;
}
//-------------------------------------------------------------------------------------
